package Data;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Random;
import java.util.Scanner;

import javax.swing.JOptionPane;

/**
 * 
 * @author Brandon James Talbot
 * 
 *         This class contains Data read in from the Pulsar files Generated by
 *         the Cuda Synocoidal algorithm
 */
public class DataArray {
	// Global arrays
	private float[][][][] data; // array of data
	private String[] DimensionNames; // names for each dimension
	private float[] minData; // min data value
	private float[] maxData; // max data value

	/**
	 * Generate a random data set
	 * 
	 * @param dim1
	 *            length of first dimension
	 * @param dim2
	 *            length of second dimension
	 * @param dim3
	 *            length of third dimension
	 * @param dim4
	 *            length of forth dimension
	 * @param names
	 *            The names of the dimensions [4];
	 */
	public DataArray(int dim1, int dim2, int dim3, int dim4, String[] names) {
		data = new float[dim1][dim2][dim3][dim4];

		DimensionNames = names;

		Random r = new Random(System.currentTimeMillis());

		for (int i = 0; i < dim1; ++i)
			for (int x = 0; x < dim2; ++x)
				for (int y = 0; y < dim3; ++y)
					for (int v = 0; v < dim4; ++v) {
						data[i][x][y][v] = r.nextFloat() * 1001;
					}
		calculateData();
	}

	/**
	 * @author Brandon James Talbot
	 *
	 * Runnable class to load files in background so JFrames Draw
	 */
	class loader implements Runnable {
		String fname;
		DataArray data;

		public loader(DataArray data, String fileName) {
			fname = fileName;
			this.data = data;
		}

		@Override
		public void run() {
//			long ttt = System.currentTimeMillis();
			Scanner s;
			try {
				s = new Scanner(new FileInputStream(fname));
				data.minData = new float[5];
				data.maxData = new float[5];
				data.DimensionNames = new String[4];
				int[] dimSize = new int[4];
				for (int i = 0; i < 4; ++i) {
					String line = s.nextLine();
					String[] split = line.split(",");
					data.DimensionNames[i] = split[0];
					dimSize[i] = Integer.parseInt(split[1]);
					// System.out.println(dimSize[i]);
					data.minData[i] = Float.parseFloat(split[2]);
					data.maxData[i] = Float.parseFloat(split[3]);
				}
				data.data = new float[dimSize[0]][dimSize[1]][dimSize[2]][dimSize[3]];
				data.minData[4] = s.nextFloat();
				data.maxData[4] = s.nextFloat();
				s.nextFloat();
				for (int i = 0; i < dimSize[0]; ++i) {
					// System.out.println(i/(float)dimSize[0]);
					for (int j = 0; j < dimSize[1]; ++j)
						for (int k = 0; k < dimSize[2]; ++k)
							for (int l = 0; l < dimSize[3]; ++l) {
								data.data[i][j][k][l] = s.nextFloat();
							}
				}
				s.close();
				
//				InputStream input = new FileInputStream(fname);
//				byte[] num = new byte[4];
//				byte[] str;
//				data.minData = new float[5];
//				data.maxData = new float[5];
//				data.DimensionNames = new String[4];
//				int[] dimSize = new int[4];
//				for (int i = 0; i < 4; ++i){
//					input.read(num);
//					str = new byte[ByteBuffer.wrap(num).getInt()];
//					input.read(str);
//					data.DimensionNames[i] = new String(str);
//					input.read(num);
//					dimSize[i] = ByteBuffer.wrap(num).getInt();
//					input.read(num);
//					data.minData[i] = ByteBuffer.wrap(num).getFloat();
//					input.read(num);
//					data.maxData[i] = ByteBuffer.wrap(num).getFloat();
//				}
//				data.data = new float[dimSize[0]][dimSize[1]][dimSize[2]][dimSize[3]];
//				input.read(num);
//				data.minData[4] = ByteBuffer.wrap(num).getFloat();
//				input.read(num);
//				data.maxData[4] = ByteBuffer.wrap(num).getFloat();
//				
//				for (int i = 0; i < dimSize[0]; ++i) {
//					// System.out.println(i/(float)dimSize[0]);
//					for (int j = 0; j < dimSize[1]; ++j)
//						for (int k = 0; k < dimSize[2]; ++k)
//							for (int l = 0; l < dimSize[3]; ++l) {
//								input.read(num);
//								data.data[i][j][k][l] = ByteBuffer.wrap(num).getFloat();
//							}
//				}
//				
//				input.close();
				
			
//				Path path = Paths.get(fname);
//				ByteBuffer bb = ByteBuffer.wrap(Files.readAllBytes(path));
//				bb.order(ByteOrder.LITTLE_ENDIAN);
//			    
//			    data.minData = new float[5];
//				data.maxData = new float[5];
//				data.DimensionNames = new String[4];
//				int[] dimSize = new int[4];
//				
//				for (int i = 0; i < 4; ++i){
//					byte[] str = new byte[bb.getInt()];
//					bb.get(str);
//					data.DimensionNames[i] = new String(str);
//					dimSize[i] = bb.getInt();
//					data.minData[i] = bb.getFloat();
//					data.maxData[i] = bb.getFloat();
//				}
//				
//				data.data = new float[dimSize[0]][dimSize[1]][dimSize[2]][dimSize[3]];
//				data.minData[4] = bb.getFloat();
//				data.maxData[4] = bb.getFloat();
//				
//				for (int i = 0; i < dimSize[0]; ++i) {
//					// System.out.println(i/(float)dimSize[0]);
//					for (int j = 0; j < dimSize[1]; ++j)
//						for (int k = 0; k < dimSize[2]; ++k)
//							for (int l = 0; l < dimSize[3]; ++l) {
//								data.data[i][j][k][l] = bb.getFloat();
//							}
//				}
			} catch (FileNotFoundException e) {
				JOptionPane.showMessageDialog(null,
						"File could not be found.", "Error",
						JOptionPane.ERROR_MESSAGE);
			} catch (IOException e) {
				e.printStackTrace();
			}
//			long tttt = System.currentTimeMillis();
			
//			System.out.println(tttt - ttt);
			
			DataHolder.stopLoading();
		}
	}

	/**
	 * Method to load files
	 * @param filename The name of the file to load
	 */
	public void loadFile(String filename) {
		DataHolder.startLoading();
		Thread T = new Thread(new loader(this, filename));
		T.start();
	}

	/**
	 * Sets the data to the given array
	 * 
	 * @param data
	 *            4D float array
	 * @param names
	 *            Array of names for each dimension
	 */
	public DataArray(float[][][][] data, String[] names) {
		this.data = data;
		DimensionNames = names;
		calculateData();
	}

	/**
	 * Calculates the data (this is a debug method since it would usually read
	 * everything from a file
	 */
	private void calculateData() {
		minData = new float[5];
		maxData = new float[5];
		for (int i = 0; i < 4; ++i) {
			minData[i] = 0;
		}

		maxData[0] = data.length - 1;
		maxData[1] = data[0].length - 1;
		maxData[2] = data[0][0].length - 1;
		maxData[3] = data[0][0][0].length - 1;

		minData[4] = Float.MAX_VALUE;
		maxData[4] = Float.MIN_VALUE;

		for (int i = 0; i < data.length; ++i)
			for (int x = 0; x < data[i].length; ++x)
				for (int y = 0; y < data[i][x].length; ++y)
					for (int v = 0; v < data[i][x][y].length; ++v) {
						minData[4] = Math.min(minData[4], data[i][x][y][v]);
						maxData[4] = Math.max(maxData[4], data[i][x][y][v]);
					}
	}

	/**
	 * Getter for data array
	 * 
	 * @return 4D float array of temperature values
	 */
	public final float[][][][] getData() {
		return data;
	}

	/**
	 * Getter for the minimum data element of each dimension
	 * 
	 * @param index
	 *            The index of the dimension
	 * @return float value of minimum temperature for dimension
	 */
	public float getMinData(int index) {
		return minData[index];
	}

	/**
	 * Getter for maximum element of each dimension
	 * 
	 * @param index
	 *            Index of the dimension
	 * @return Float value of maximum temperature for dimension
	 */
	public float getMaxData(int index) {
		return maxData[index];
	}

	/**
	 * Gets the name of the dimension
	 * 
	 * @param index
	 *            index of dimension
	 * @return name of the dimension specified
	 */
	public String getDimensionName(int index) {
		return DimensionNames[index];
	}

	/**
	 * Gets the length of the dimension
	 * 
	 * @param index
	 *            the index of the dimension (only 4 (0-3))
	 * @return The number of elements in the dimension
	 */
	public int getLength(int index) {
		switch (index) {
		case 0:
			return data.length;
		case 1:
			return data[0].length;
		case 2:
			return data[0][0].length;
		case 3:
			return data[0][0][0].length;
		default:
			return 0;
		}
	}
}